#pragma once

#include <ddblk/apds/combine_int.h>
#include <dbg.h>

/* 标准combing, 主要思想是降低通信开销瓶颈 */
/* 使用线程数组 */
/* 批量完成线程 */
/* 批量通知线程降低通讯开销 */
/* 不需要节点还是? */
#include <ddblk/apds/combine/ccsync.h>
typedef _ccsyncnode_t _qcmbnnode_t;
typedef struct _qcmbn {
	struct{
		lkword_t lock;
		/* 用于帮助 */
		_ccsyncnode_t *last_helped;
		_ccsyncnode_t *tail;
	} CACHE_ALIGNED;
	/* 用于入队 */
} PAGE_ALIGNED _qcmbn_t;




DECL_COMBINE(qcmbn)

SAI void __qcmbn_init(_qcmbn_t *s, _qcmbnnode_t *sentry)
{
	s->lock = 0;
	s->last_helped = sentry;
	s->tail = sentry;
	sentry->next = NULL;
	sentry->flag = COMBINE_FLG_OWNED;
	
}
SAI void *__qcmbn_destruct(_qcmbn_t *s, _qcmbnnode_t **psentry){
	ASSERT(s->tail != NULL);
	*psentry = s->tail;
	return (void *)s;
}

/* 原子加原子减开销太大，是否能用ffwd那种更简单的通知机制? */
/* 提交请求 通知远程, 完成请求通知线程 */
SAI _qcmbnnode_t *__qcmbn_fetch(_qcmbn_t *s, apdsfunc_t *pfunc, apdsarg_t *args, _qcmbnnode_t *node) {
	apds_movargs(&node->args, args);
	node->func = pfunc;

	/* 确保已经清空 */
	node->flag = COMBINE_FLG_WAIT;
	node->next = NULL;

	/* 直接入队 */
	_qcmbnnode_t *cur_node = swp_rel(&s->tail, node);
	atm_st(&cur_node->next, node);

	return cur_node;
}

SAI apdsret_t __qcmbn_wait(_qcmbn_t *s, _qcmbnnode_t *node, INFUN(cmbn_wait, waitpolicy)){
	waitcnt_t cnt = -1;
	lkword_t locked = 1;
	lkword_t unlocked = 0;
	lkword_t lock;
	lkflag_t pending;
	lock = atm_ld(&s->lock);
	do {
		pending = atm_ld(&node->flag);
		while (lock && pending)
		{
			cnt = waitpolicy->waitf(&s->lock, &locked, cnt);
			lock = atm_ld(&s->lock);
			pending = atm_ld(&node->flag);
		}
		if (!pending){
			mp_rmb();
			return apds_args_ret(&node->args);
		}
		if(!lock){
			if (cas_acq(&s->lock, &lock, 1))
				break;
		}
	} while(TRUE);

	_qcmbnnode_t *cur = atm_ld(&s->last_helped);

	
	_qcmbnnode_t *tmp_node = cur;
	_qcmbnnode_t *tmp_node_next = cur->next;
	
	do
	{
		apds_set_ret(&tmp_node->args, apds_sync_single(tmp_node_next->func, &tmp_node_next->args));
		atm_strel(&tmp_node->flag, COMBINE_FLG_COMPLETED);
		
		tmp_node = tmp_node_next;
		tmp_node_next = tmp_node->next;
		if (tmp_node_next == NULL){
			
			if (tmp_node == atm_ld(&s->tail)){
				break;
			}
			else{
				/* 等待tmp_node_next不为空 */
				do{
					PAUSE();
					tmp_node_next = atm_ld(&tmp_node->next);
				} while(!tmp_node_next);
			}
		}
	} while (TRUE);

	atm_st(&s->last_helped, tmp_node);
	atm_strel(&s->lock, 0);
	
	waitpolicy->wakef(&s->lock, &unlocked);
	return apds_args_ret(&node->args);
}

PARAM_NONULL(1) SAI _qcmbn_t *_qcmbn_init(void *ptr, size_t size, INFUN(cmbn_node, nodemgr))
{
	if (size < sizeof(_qcmbn_t)) return NULL;
	_qcmbnnode_t *node = (_qcmbnnode_t *)nodemgr->get_lknode(sizeof(_qcmbnnode_t));
	_qcmbn_t *f = (_qcmbn_t *)ptr;
	__qcmbn_init(f, node);
	return f;
}
PARAM_NONULL(1) SAI void *_qcmbn_destruct(_qcmbn_t *f, INFUN(cmbn_node, nodemgr)){
	_qcmbnnode_t *node;
	void *rptr = __qcmbn_destruct(f, &node);
	nodemgr->ret_lknode(node, sizeof(_qcmbnnode_t));
	return rptr;
}
PARAM_NONULL(1, 2, 3) SAI _qcmbnnode_t *_qcmbn_fetch(_qcmbn_t *f, apdsfunc_t *pfunc, apdsarg_t *args, apdstinfo_t *tinfo, INFUN(cmbn_node, nodemgr)){
	PARAM_USED(tinfo);
	_qcmbnnode_t *node = (_qcmbnnode_t *)nodemgr->get_lknode(sizeof(_qcmbnnode_t));
	return __qcmbn_fetch(f, pfunc, args, node);
}
PARAM_NONULL(1, 2) SAI apdsret_t _qcmbn_wait(_qcmbn_t *f, _qcmbnnode_t *node, apdstinfo_t *tinfo, INFUN(cmbn_wait, waitpolicy), INFUN(cmbn_node, nodemgr)){
	PARAM_USED(tinfo, nodemgr);
	apdsret_t ret = __qcmbn_wait(f, node, waitpolicy);
	nodemgr->ret_lknode(node, sizeof(_qcmbnnode_t));
	return ret;
}
